#pragma once
#include <Arbitrator.h>
#include <BWAPI.h>
#include <BWTA.h>
#include <queue>
#include "EngageTypes.h"
using namespace BWAPI;

// typedef for easier access
typedef EngageTypes::EngageType EngageType;

class UnitTactics
{
  public:
    // UnitState: Holds information about each unit and data relevant to this
    // engagement. Note that other Tactics classes have the option of inheriting
    // from this struct in order to extend it with more members. They may also
    // override newState and delState which are called when a new state is needed.
    struct UnitState {
      Unit* unit;
      Position target;
      EngageType method;
      int priority; // unused as of now
    };
    typedef UnitState typeState;

    // Action: Holds information about the priorities of each unit. Because you
    // can be reacting to either a Unit or a Bullet, the Action class is the
    // base and each type extends it below. There should be no instance of Action
    class Action {
      public:
        enum type {UNIT, BULLET};
        type actionType;
        int rangeDist;
        int priority;
        Action(int r, int p, type t) : rangeDist(r), priority(p), actionType(t) {};

        // Comparison operator overloading for sorting in the set
        bool operator<(const Action& other) const {
          return this->priority > other.priority;
        }
    };
    class UnitAction : public Action {
      public:
        UnitType target;
        int (*conditionTest)(UnitState* engager, std::set<Unit*> triggers);

        // Constructor using implicit initialization
        UnitAction(
          UnitType t,
          int r,
          int (*c)(UnitState*, std::set<Unit*>),
          int p 
        ) : target(t), conditionTest(c), Action(r, p, UNIT) {};
    };
    class BulletAction : public Action {
      public:
        BulletType target;
        int (*conditionTest)(UnitState* engager, std::set<Bullet*> triggers);

        // Constructor using implicit initialization
        BulletAction(
          BulletType t,
          int r,
          int (*c)(UnitState*, std::set<Bullet*>),
          int p
        ) : target(t), conditionTest(c), Action(r, p, BULLET) {};
    };

    //UnitTactics(UnitType type);
    UnitTactics();
    virtual ~UnitTactics();
    virtual typeState* newState();
    virtual void delState(typeState* state);
    virtual void addPriority(Action *p);
    static bool deletePriority(Action* p);
    virtual void update(UnitState* state);
    virtual std::set<Unit*> scanUnitRadius(UnitState* engager, UnitType target, int radius);
    virtual std::set<Bullet*> scanUnitRadius(UnitState* engager, BulletType target, int radius);

    // Some sample functions
    static int fallback(UnitState* engager, std::set<Unit*> triggers);
    static int default(UnitState* engager, std::set<Unit*> triggers);
    static int attack(UnitState* engager, std::set<Unit*> triggers);
    static int avoid(UnitState* engager, std::set<Unit*> triggers);

  private:
    //UnitType type;
    std::set<Action*> priority;
};

